; nasm -f bin _5.asm -o _5.bin
; qemu-img create _5.img 10m
; dd if=_5.bin of=_5.img bs=512 count=1 conv=notrunc
; qemu-system-i386 -fda _5.img

; qemu-system-i386 _5.bin

[bits 16]

org 0x7c00

xor ax, ax
mov cx, ax
mov bx, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7c00

global _start

_start:
    mov di, 0	; 初始化界面
    mov al, 3   ; 设置显示模式	AH=00H	AL=显示模式	AL=显示模式标志/CRT控制模式字节
    int 10h

    mov ah, 6h
    mov al, 0
    mov cx, 0
    mov bh, 0x01 ; 黑色背景
    mov dx, 0xffff
    int 10h
    jmp first

first:
    mov ah, 2h	; 设置初始光标
    mov dh, 0   ; 行
    mov dl, 0   ; 列
    int 10h

    mov ax, id	
    mov bp, ax	; 写字符串（EGA+，最低PC AT ）	AH=13H	AL=写模式，BH=页码，BL=颜色，CX=字符串长度，DH=行，DL=列，ES:BP=字符串偏移量
    mov cx, 8
    mov ah, 13h
    mov al, 1   ; 写模式
    mov dh, 13 ; 根据计算，这是主屏幕中间位置， 因为 25/2 = 13
    mov dl, 36  ;  因为 80/2 - 8/2 = 36
    mov bh, 0 ; 第一页
    mov bl, 0x0f ; 白色
    int 10h

    mov ax, continue_string	   
    mov bp, ax		
    mov cx, 28
    mov ah, 13h
    mov al, 1
    mov dh, 14
    mov dl, 26
    mov bh, 0
    mov bl, 0x0f
    int 10h

mov ah, 0h
int 16h ; 等待键盘输入
jmp second

second:
    ; 向上滚动窗口	AH=06H	AL=滚动的行（0=清除，被用于CH，CL，DH，DL），
    ; BH=背景颜色和前景颜色，BH=4(背景）3（前景）H，意义为背景颜色为红色，前景颜色为青色。请参考 BIOS颜色属性。
    ; CH=高行数，CL=左列数，DH=低行数，DL=右列数
    xor ax, ax ; 初始化寄存器为0
    mov bx, ax
    mov cx, ax
    mov dx, ax
    xor di, di
    ; mov si, start_char ; 禁用，因为主屏幕不需要各种提示语

    mov ah, 6h ; 初始化界面
    mov al, 0
    mov bh, 0x01  ; 设置背景黑色
    mov cx, 0
    mov dx, 0xffff
    int 0x10

    ; 设置光标位置	AH=02H	BH=页码，DH=行，DL=列
    mov ah, 2h
    mov bh, 0
    mov dh, 0x02 ; 行
    mov dl, 0x00 ; 列
    int 0x10

    mov si, id
    call down_right

down_right:
    call delay		; 延时函数
	call rand

	mov ah, 9h		
	mov al, [si]
    cmp al, 'e'
    jne continue_1		
	mov si, id		
	mov al, [si]
continue_1:
    inc si			
	mov cx, 1		; 输出一次
	int 10h

	call double		; 双向输出函数
	inc dl			; 改变列
	inc dh			; 改变行
	cmp dh, 25		; 与屏幕行边界判断进行下一个打印函数
	jge up_right		
	cmp dl, 80		; 与屏幕列边界判断进行下一个打印函数
	jge down_left
	
	mov ah, 2h		; 设置改变后的坐标
	int 10h	

	jmp down_right		; 该输出函数循环

up_right:
	call delay
	call rand

	mov ah, 9h
	mov al, [si]
    cmp al, 'e'
    jne continue_2		
	mov si, id		
	mov al, [si]
continue_2:
	inc si
	mov cx, 1
	int 10h

	call double

	dec dl
	inc dh

	cmp dl, 0
	jle up_left
	cmp dh, 25
	jge down_right

	mov ah, 2h
	int 10h	
	jmp up_right

up_left:
	call delay
	call rand

	mov ah, 9h
	mov al, [si]
    cmp al, 'e'
    jne continue_3		
	mov si, id		
	mov al, [si]
continue_3:
	inc si
	mov cx, 1
	int 10h

	call double
	
	dec dh
	dec dl

	cmp dh, 0
	jl down_left
	cmp dl, 0
	jl up_right

	mov ah, 2h
	int 10h
		
	jmp up_left

down_left:
	call delay		
	call rand
	mov ah, 9h
	mov al, [si]
    cmp al,'e'
    jne continue_4		
	mov si, id		
	mov al, [si]
continue_4:
	inc si
	mov cx, 1
	int 10h
    
	call double

	inc dl
	dec dh

	cmp dh, 0
	jl up_left
	cmp dl, 80
	jge down_right

	mov ah, 2h
	int 10h
		
	jmp down_left
;双向输出函数
double :
	mov ah, 3h	; 取当前光标
	int 10h

	push dx		; 压入当前光标
	push cx

	mov ch, 24	; 24是屏幕最大行数, 行对称
	mov cl, 79	; 79是屏幕最大列数, 列对称

	sub cl, dl	; 计算对称列
	sub ch, dh  ; 计算对称行

	mov dl, cl  ; 对称列
	mov dh, ch  ; 对称行

	mov ah, 2h	; 设置光标
	int 10h	

	mov ah, 9h	; 输出字符
	mov al, [si]
    cmp al,'e'
    jne continue_5		
	mov si, id		
	mov al, [si]
continue_5:
	inc si		
	mov cx, 1  ; 输出一次
	int 10h 
	pop cx		; 压出旧的光标参数覆盖对称临时使用的
	pop dx
	ret			; 返回

delay :
    ; 中断号：15H
	; 入口参数：
	;（1） AH：86H
	;（2） CX：DX= 延时时间（单位是微秒），CX是高字，DX是低字		
	mov ah, 86h			; 15号中断86h的延时功能,延迟50000ms
   	mov cx, 0x00 
    mov dx, 0xc350     
    int 15h
	ret

rand :
	; 	入口参数：AH＝00H
	;	出口参数：AL＝00H—未过午夜，否则，表示已过午夜
    ;   CX:DX(我所需要的数据)＝时钟“滴答”计数
	mov ah, 0	; 运用时钟取数中断随机取时间值
	int 0x1a
	mov ax, dx	; 对时钟dx进行操作，用函数获取随机数
	or ah, 34
	push cx
	mov cl, 217
	div cl
	mov bl, ah	;将随机数存在bl中
	pop cx
	ret

data:
    id : db "22320131"
    end_char : db "end"
    continue_string : db "Press any key to continue..."

	
jmp $


times 510-($-$$) db 0
dw 0xaa55
